package com.pat.jrgwt.client;

import java.util.Date;
import java.util.HashMap;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.pat.jrgwt.shared.MyConstants;

public class DvSharpeRatioPor3 extends Composite
{
    
    public class DataColumns
    {
        public static final int Performance = 0;
        public static final int LeverageAdaption = 1;
        public static final int RiskFreeInterest = 2;
        public static final int RiskFreeInterestPerMonth = 3;
        public static final int AdditionalPerformance = 4;
        public static final int VarAdditionalPerformance = 5;
        public static final int VarPerformance = 6;
        public static final int DownsideVolationCalculation = 7;
    };

//    @UiField Grid DvRatioTotals;
    /**
     * Displays main Sharpe ratio values
     */
    protected FlexTable m_table = new FlexTable();
    
    /**
     * Displays statistics for whole Sharpe ratio table
     */
    protected FlexTable m_statsTable = new FlexTable();

    /**
     * Formats double to string "###,##0.000"
     */
    protected NumberFormat m_numberFormat3 = NumberFormat.getFormat("###,##0.000");

    
    protected HashMap<Integer, double[]> m_arRisks = new HashMap<Integer, double[]>();
    
    protected String[] m_arHeaders = new String[] { "Year", "Month", "Performance (levered, %)", "Leverage adaption", "risk free interest (%)", "risk free interest per month (%)", "Additional performance (%)", "VAR (additional performance)", "VAR (Performance)", "Downside Vola. Calculation" };

    /**
     * Stores all calculated data
     */
    protected double[][] m_arCellValues = null;
    
    /**
     * Stores statistics for Sharpe ratio
     */
    protected DvRatioStats m_statisticsValues = new DvRatioStats();
    
    protected int m_totalYear = 6;
    @SuppressWarnings("deprecation")
    protected int m_currentYear = new Date().getYear();

    public DvSharpeRatioPor3()
    {
        VerticalPanel p = new VerticalPanel();
        p.add(m_table);
        p.add(new Label("Statistics"));
        p.add(m_statsTable);
        initWidget(p);
        
        formatDvRatio();
        m_arCellValues = new double[12 * m_totalYear][getDataColumns()];
    }

    /**
     * Returns number of data columns or 0. It is safe to call this method even if null
     */
    public int getDataColumns()
    {
        return m_arHeaders == null ? 0 : m_arHeaders.length < 3 ? 0 : m_arHeaders.length - 2;
    }

    public void recalculate()
    {
        int row = 1;
        double risk = 0;
        double perf = 0;
        double leverageAdaption = 0;
        double perfLev = 0;
        double riskPerMonth = 0;
        double additionalPerf = 0;
        double downsideVolaCalculation = 0;
        
        double varAdditionalPerf = 0;
        double varPerformance = 0;
        
        final double initialPerf = 1;
        final double initialLeverageAdaption = 1;
        NumberFormat nf = NumberFormat.getFormat("###,##0.00");
        
//        DvRatioStats m_statisticsValues = new DvRatioStats();
        
        // Get data from ArchivePerf
        ArchivePerfPor3 archivePerv = Por3.getInstance().archivePerformance;
        
        // Row in cell values
        int dataRow = 0;
        
        for (int i = 0; i < m_totalYear; i++)
        {
            int year = 1900 + m_currentYear - m_totalYear + i;
            m_table.setText(row, 0, "" + year);
            for (int month = 1; month < 13; month++)
            {
                m_table.setText(row, 1, "" + month);
                
//                GWT.log("Getting risk for " + year + "-" + month);
                risk = getRiskByDate(year, month);
//                GWT.log("Risk is: " + risk);
                if( risk != Double.MIN_VALUE )
                {
//                    GWT.log("Getting archive performance for: " + year + "-" + month);
                    perf = archivePerv.getPerfByDate(year, month);
                    m_arCellValues[dataRow][DataColumns.Performance] = perf;
                    if( perf != Double.MIN_VALUE )
                    {
                        perfLev = initialPerf * perf;
                    }else
                    {
                        perfLev = 0;
                    }
                    
                    leverageAdaption = initialLeverageAdaption * perf;

                    riskPerMonth = risk / 12;
                    additionalPerf = perfLev - riskPerMonth;
                    m_arCellValues[dataRow][DataColumns.AdditionalPerformance] = additionalPerf;
                    downsideVolaCalculation = additionalPerf < 0 ? additionalPerf * additionalPerf : 0;
                    
                    m_table.setText(row, 2, nf.format(perfLev));
                    m_table.setText(row, 3, m_numberFormat3.format(leverageAdaption));
                    m_table.setText(row, 4, m_numberFormat3.format(risk));
                    m_table.setText(row, 5, m_numberFormat3.format(riskPerMonth));
                    m_table.setText(row, 6, m_numberFormat3.format(additionalPerf));
                    m_table.setText(row, 9, m_numberFormat3.format(downsideVolaCalculation));
                    
                    // Calculate statistics
                    m_statisticsValues.totalAdditionalPerf += additionalPerf;
                    m_statisticsValues.totalLeverageAdaption += leverageAdaption;
                    m_statisticsValues.totalMonthlyReturn += perfLev;
                    m_statisticsValues.totalDownsideVolatility += downsideVolaCalculation;
                    m_statisticsValues.totalRiskPerMonth += riskPerMonth;
                    
                    if( m_statisticsValues.maxMonthlyReturn < perfLev )
                    {
                        m_statisticsValues.maxMonthlyReturn = perfLev;
                    }
                    if( m_statisticsValues.minMonthlyReturn > perfLev )
                    {
                        m_statisticsValues.minMonthlyReturn = perfLev;
                    }
                    m_statisticsValues.totalRecords++;
                    
                }else  // END IF ( risk is not undefined )
                {
                    for (int j = 2; j < m_table.getCellCount(0); j++)
                    {
                        m_table.setText(row, j, "");
                    }
                }
                
                row++;
                dataRow++;
            }
        }  // END FOR ( years )
        
//        stats.averageMonthlyReturn = stats.totalMonthlyReturn / stats.totalRecords;
//        stats.averageRiskPerMonth = stats.totalRiskPerMonth / stats.totalRecords;
//        stats.averageAdditionalPerf = stats.totalAdditionalPerf / stats.totalRecords;
//        stats.averageDownsideVolatility = stats.totalDownsideVolatility / stats.totalRecords;
        
        dataRow = 0;
        for(int i = 0; i < m_totalYear; i++)
        {
//            int year = 1900 + m_currentYear - m_totalYear + i;
            for (int month = 1; month < 13; month++)
            {
                varAdditionalPerf = Math.pow(m_statisticsValues.getAverageAdditionalPerf() + m_arCellValues[dataRow][DataColumns.AdditionalPerformance], 2);
                m_table.setText(dataRow + 1, 7, m_numberFormat3.format(varAdditionalPerf));
                m_statisticsValues.totalVarAdditionalPerfomance += varAdditionalPerf;
                
                varPerformance = Math.pow(m_arCellValues[dataRow][DataColumns.Performance] - m_statisticsValues.getAverageMonthlyReturn(), 2);
                m_table.setText(dataRow + 1, 8, m_numberFormat3.format(varPerformance));
                m_statisticsValues.totalVarPerfomance += varPerformance;
                
                dataRow++;
            }
            
        }
        
        GWT.log("DRAWING AVERAGES FRO SHARPE RATIO POR3");
        // Display averages
        m_table.setText(row, 1, "Averages");
        m_table.setText(row, 2, m_numberFormat3.format(m_statisticsValues.getAverageMonthlyReturn()));
        m_table.setText(row, 3, m_numberFormat3.format(m_statisticsValues.getAverageLeverageAdaption()));
        m_table.setText(row, 5, m_numberFormat3.format(m_statisticsValues.getAverageRiskPerMonth()));
        m_table.setText(row, 6, m_numberFormat3.format(m_statisticsValues.getAverageAdditionalPerf()));
        m_table.setText(row, 7, m_numberFormat3.format(m_statisticsValues.getAverageVarAdditionalPerfomance()));
        m_table.setText(row, 8, m_numberFormat3.format(m_statisticsValues.getAverageVarPerfomance()));
        m_table.setText(row, 9, m_numberFormat3.format(m_statisticsValues.getAverageDownsideVolatility()));
        for( int i = 0; i < 9; i++ )
        {
            m_table.getCellFormatter().setStylePrimaryName(row, i + 1, "jrCellHeader");
        }

        drawStatistics();
    }

    /**
     * Draw statistics for calculated table
     */
    protected void drawStatistics()
    {
        int row = 0;
        
        m_statsTable.setText(row, 6, "St.Dev. (Performance, month):");
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 6, "jrCellHeader");
        m_statsTable.getCellFormatter().setWidth(row, 6, "300px");
        m_statsTable.setText(row, 7, m_numberFormat3.format( m_statisticsValues.getStDevPerformanceYear() ));
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 7, "jrCellHeader");
        m_statsTable.getCellFormatter().setWidth(row, 7, "100px");
        row++;

        m_statsTable.setText(row, 6, "Sum (add. performance, year)/(n-1):");
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 6, "jrCellHeader");
        m_statsTable.setText(row, 7, m_numberFormat3.format( m_statisticsValues.getSumAddPerfomanceYear() ));
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 7, "jrCellHeader");
        row++;

        m_statsTable.setText(row, 6, "St.Dev. (add. Performance, year):");
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 6, "jrCellHeader");
        m_statsTable.setText(row, 7, m_numberFormat3.format(Math.sqrt(m_statisticsValues.getSumAddPerfomanceYear())));
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 7, "jrCellHeader");
        row++;

        m_statsTable.setText(row, 6, "Root(AVG(Downside value calc.)");
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 6, "jrCellHeader");
        m_statsTable.setText(row, 7, m_numberFormat3.format( m_statisticsValues.getRootAvgDownsideVolatility() ));
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 7, "jrCellHeader");
        row++;
        
        m_statsTable.setText(row, 7, m_numberFormat3.format( (m_statisticsValues.getAverageMonthlyReturn() - m_statisticsValues.getAverageRiskPerMonth()) / m_statisticsValues.getRootAvgDownsideVolatility() ));
        row++;

        
        m_statsTable.setText(row, 6, "Max Monthly Return:");
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 6, "jrCellHeader");
        m_statsTable.setText(row, 7, m_numberFormat3.format(m_statisticsValues.maxMonthlyReturn));
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 7, "jrCellHeader");
        row++;
        
        m_statsTable.setText(row, 6, "Min Monthly Return:");
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 6, "jrCellHeader");
        m_statsTable.setText(row, 7, m_numberFormat3.format(m_statisticsValues.minMonthlyReturn));
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 7, "jrCellHeader");
        row++;
        
        m_statsTable.setText(row, 6, "Avg Monthly Return:");
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 6, "jrCellHeader");
        m_statsTable.setText(row, 7, m_numberFormat3.format(m_statisticsValues.getAverageMonthlyReturn()));
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 7, "jrCellHeader");
        row++;
        
        m_statsTable.setText(row, 6, "Avg Return on Investment p.a.:");
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 6, "jrCellHeader");
        m_statsTable.setText(row, 7, m_numberFormat3.format(m_statisticsValues.getAverageMonthlyReturn() * 12));
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 7, "jrCellHeader");
        row++;

        m_statsTable.setText(row, 6, "Drawdown:");
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 6, "jrCellHeader");
        m_statsTable.setText(row, 7, m_numberFormat3.format(0));
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 7, "jrCellHeader");
        row++;

        m_statsTable.setText(row, 6, "St.Dev. (Performance, year):");
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 6, "jrCellHeader");
        m_statsTable.setText(row, 7, m_numberFormat3.format( m_statisticsValues.getStDevPerformanceYear() * MyConstants.SQRT_12 ));
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 7, "jrCellHeader");
        row++;

        m_statsTable.setText(row, 6, "Downside Volatility:");
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 6, "jrCellHeader");
        m_statsTable.setText(row, 7, m_numberFormat3.format( m_statisticsValues.getRootAvgDownsideVolatility() * MyConstants.SQRT_12 ));
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 7, "jrCellHeader");
        row++;

        m_statsTable.setText(row, 6, "Sharpe-Ratio:");
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 6, "jrCellHeader");
        m_statsTable.setText(row, 7, m_numberFormat3.format( m_statisticsValues.getAverageAdditionalPerf() / m_statisticsValues.getStDevAddPerfomanceYear() * MyConstants.SQRT_12 ));
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 7, "jrCellHeader");
        row++;

        m_statsTable.setText(row, 6, "Sortino-Ratio:");
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 6, "jrCellHeader");
        m_statsTable.setText(row, 7, m_numberFormat3.format( m_statisticsValues.getSortinoRatio() ));
//        m_statsTable.getCellFormatter().setStylePrimaryName(row, 7, "jrCellHeader");
        row++;
    }

    protected double getRiskByDate(int year, int month)
    {
        double value = Double.MIN_VALUE;
        
        if( m_arRisks.containsKey(year) )
        {
            if( (month > 0) && (month < 13) )
            {
                value = m_arRisks.get(year)[month - 1];
            }
        }
        
        return value;
    }
    
    public void setData(HashMap<Integer, double[]> arRisks)
    {
        GWT.log("DV Sharpe ratio - setting risks...");
        m_arRisks = arRisks;
        double[] ar = new double[] { 2.340, 2.340, 2.580, 2.590, 2.600, 2.780, 2.830, 3.010, 3.060, 3.330, 3.320, 3.570 };
//        ar[0] = 1.06;
//        ar[1] = 2.06;
        m_arRisks.put(2006, ar);
    }
    
//    protected void loadData()
//    {
//        JsonRequest.get(MyConstants.EXCHANGE_SERVER_URL + "pet/getdvratio?callback=", new JsonRequestHandler()
//        {
//            
//            @Override
//            public void onRequestCompleted(JavaScriptObject json)
//            {
//                JrcEventsLog.getInstance().add("Got JSON data for DvRatio...");
//                if( json != null )
//                {
//                    m_arRisks = DvRatio.parseJsonData(json);
//                    recalculateData();
//                }
//            }
//        });
////        m_arRisks.put(2004, new double[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 });
//        
//    }

    /**
     * Parses JSON data from server and return HashMap
     * @param json
     * @return
     */
    public static HashMap<Integer, double[]> parseJsonData(JavaScriptObject json)
    {
        JSONObject jsonObj = new JSONObject(json);
        HashMap<Integer, double[]> arRisks = new HashMap<Integer, double[]>();
        try
        {
            int year = 0;
            double[] values = null;
            GWT.log("Parsing JSON data for DV ratio...");
            for( String key : jsonObj.keySet() )
            {
                year = Integer.parseInt(key);
                values = new double[12];
                JSONArray ar = jsonObj.get(key).isArray();
                for(int month = 0; month < ar.size(); month++)
                {
                    values[month] = Double.parseDouble(ar.get(month).toString().replaceAll("\"", ""));
                }
                arRisks.put(year, values);
            }
            GWT.log("DV ratio JSON parsing is finished");
        }
        catch( Exception e )
        {
            GWT.log("Error parsing DV ratio JSON from ITTC server! " + e.toString());
        }
        
        return arRisks;
    }

    
    public void formatDvRatio()
    {
        m_table.setBorderWidth(1);
        
        for (int i = 0; i < m_arHeaders.length; i++)
        {
            m_table.setText(0, i, m_arHeaders[i]);
            m_table.getCellFormatter().setStylePrimaryName(0, i, "jrCellHeader");
        }
    }

}
